package edu.uvic.TabViz;

import java.nio.IntBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import edu.uvic.TabViz.framework.World;
import edu.uvic.TabViz.framework.WorldReplicate;
import edu.uvic.TabViz.framework.WiFi.Data;
import edu.uvic.TabViz.framework.primitives.Camera;
import edu.uvic.TabViz.framework.primitives.Object3D;

public class MainView extends GLSurfaceView implements OnGestureListener{
	private GestureDetector gestureScanner;
	private MainRenderer renderer;
	private TabVizActivity context;
	public float[] move;
	public boolean lock = false;
	public boolean done = false;
	public boolean grabTouch = false;
	public Bitmap mBitmap;
	int[] viewport = new int[16]; 
	
	public MainView(Context context, WorldReplicate ws) {
		super(context);
		this.context = (TabVizActivity)context;
		renderer = new MainRenderer(this.context,ws);
		renderer.setSurfaceView(this);
		setEGLContextClientVersion(2);
		setEGLConfigChooser(8, 8, 8, 8, 16, 8);
		setFocusableInTouchMode(true);
		gestureScanner = new GestureDetector(this);
		setRenderer(renderer);
	}

	public boolean onTouchEvent(MotionEvent me){
		gestureScanner.onTouchEvent(me);
		invalidate();
		return true;
	}

	public MainRenderer getRenderer(){
		return renderer;
	}

	@Override
	public boolean onDown(MotionEvent e) {
		return false;
	}

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
			float velocityY) {
		return false;
	}

	@Override
	public void onLongPress(MotionEvent e) {
	}

	public void onScroll(float distanceX, float distanceY) {
		if (context.thisWorld!=null)
			for (Object3D o : context.thisWorld.getObjects()){
				o.getRotation().setY(o.getRotation().getY() - distanceX/10);
				o.getRotation().setX(o.getRotation().getX() + distanceY/10);
			}
		requestRender();
	}
	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
			float distanceY) {
		if (lock) return false;
		if (grabTouch) return false;
		if (context.thisWorld!=null){
			for (Object3D o : context.thisWorld.getObjects()){
				o.getRotation().setY(o.getRotation().getY() - distanceX/10);
				o.getRotation().setX(o.getRotation().getX() + distanceY/10);
			}
			requestRender();
			if( !context.grabed && !context.sharedClient && !context.sharedServer && !context.serverSockets.isEmpty()){
				context.capture = true;
				requestRender();
				while(!done){
					try {
						Thread.sleep(5);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				for (int i = 0; i<context.serverSockets.size(); i++)
					context.serverSockets.get(i).msgOut.add(new Data(context.toBytes(mBitmap),Data.Touch));
				done = false;
			}

			if( context.client != null && (context.grabed || context.sharedClient))
				context.client.msgOut.add(new Data(new float[]{distanceX,distanceY},Data.Touch));
			if(context.sharedServer && !context.serverSockets.isEmpty())
				for (int i = 0; i<context.serverSockets.size(); i++)
					context.serverSockets.get(i).msgOut.add(new Data(new float[]{distanceX,distanceY},Data.Touch));
		}
		return false;
	}

	@Override
	public void onShowPress(MotionEvent e) {
	}
	
	public float[] GetWorldCoords( float x, float y, int w, int h, Camera c)
	   {  
	       // Initialize auxiliary variables.
	       float[] worldPos = new float[2];
	              
	       // Auxiliary matrix and vectors
	       // to deal with ogl.
	       float[] invertedMatrix, transformMatrix,
	           normalizedInPoint, outPoint;
	       invertedMatrix = new float[16];
	       transformMatrix = new float[16];
	       normalizedInPoint = new float[4];
	       outPoint = new float[4];
	 
	       // Invert y coordinate, as android uses
	       // top-left, and ogl bottom-left.
	       int oglTouchY = (int) (h - y);
	       
	       /* Transform the screen point to clip
	       space in ogl (-1,1) */       
	       normalizedInPoint[0] =
	        (float) (x * 2.0f / w - 1.0f);
	       normalizedInPoint[1] =
	        (float) ((oglTouchY) * 2.0f / h - 1.0);
	       normalizedInPoint[2] = - 1.0f;
	       normalizedInPoint[3] = 1.0f;

	       Matrix.multiplyMM(
	           transformMatrix, 0,
	           c.getProjectionMatrix(), 0,
	           renderer.getmVMatrix(), 0);
	       Matrix.invertM(invertedMatrix, 0,
	           transformMatrix, 0);       
	 
	       /* Apply the inverse to the point
	       in clip space */
	       Matrix.multiplyMV(
	           outPoint, 0,
	           invertedMatrix, 0,
	           normalizedInPoint, 0);
	       
	       if (outPoint[3] == 0.0)
	       {
	           // Avoid /0 error.
	           Log.e("World coords", "ERROR!");
	           return worldPos;
	       }
	       
	       // Divide by the 3rd component to find
	       // out the real position.
	       worldPos = new float[]{outPoint[0] / outPoint[3],
	           outPoint[1] / outPoint[3]};
	         
	       return worldPos;       
	   }

	
	@Override
	public boolean onSingleTapUp(MotionEvent e) {
		float[] xyzw = {0, 0, 0, 0};
		int[] viewport;
		if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
			viewport = new int[]{0,0,1280,752};
		else
			viewport = new int[]{0,0,800,616};
		android.opengl.GLU.gluUnProject(e.getX(), viewport[3] - e.getY(), 0, renderer.getmVMatrix(), 0, 
				context.thisWorld.getCamera().getProjectionMatrix(), 0, viewport, 0, xyzw, 0);
//		GLU.gluProject(e.getX(), - e.getY(), context.thisWorld.getCamera().getPosition().getZ(), renderer.getmVMatrix(), 0, context.thisWorld.getCamera().getProjectionMatrix(), 0, viewport, 0, xyzw, 0);
		
		
		xyzw[0] /= xyzw[3];
		xyzw[1] /= xyzw[3];
		xyzw[2] /= xyzw[3];
		Log.e("Touch X",Float.toString(xyzw[0]));
		Log.e("Touch Y",Float.toString(xyzw[1]));
		android.opengl.GLU.gluUnProject(e.getX(), viewport[3] - e.getY(), 1, renderer.getmVMatrix(), 0, 
				context.thisWorld.getCamera().getProjectionMatrix(), 0, viewport, 0, xyzw, 0);
		
		xyzw[0] /= xyzw[3];
		xyzw[1] /= xyzw[3];
		xyzw[2] /= xyzw[3];
		Log.e("Touch X",Float.toString(xyzw[0]));
		Log.e("Touch Y",Float.toString(xyzw[1]));
		 xyzw[3] = 1;
		return false;
	}
	public class MainRenderer extends edu.uvic.TabViz.framework.Renderer{
		public WorldReplicate ws;
		public boolean syncing = false;

		public MainRenderer(TabVizActivity context) {
			super(context);
		}
		public MainRenderer(TabVizActivity context, WorldReplicate ws) {
			this(context);
			mFrameRate = 60;
			if (ws != null)
				this.ws = ws;
		}
		@Override
		public void onSurfaceCreated(GL10 gl, EGLConfig config) {
			super.onSurfaceCreated(gl, config);
			if (ws == null)
				context.thisWorld = context.createWorld();
			else context.thisWorld = new World(ws);
			requestRender();
		}

		public void onDrawFrame(GL10 glUnused) {
			super.onDrawFrame(glUnused);
			android.opengl.GLES20.glGetIntegerv(GLES20.GL_VIEWPORT, viewport, 0);
			if(syncing == true){
				syncing = true;
				//				world = new World();
				//				world.set(ws);
				context.thisWorld = new World(ws); 
				Log.e("Draw frame","replicate");
				ws =null;
				syncing = false;
				requestRender();
			}else{

				int clearMask = GLES20.GL_COLOR_BUFFER_BIT;

				GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); 
				if (context.thisWorld!=null)
					GLES20.glClearColor(context.thisWorld.getBackground()[0], context.thisWorld.getBackground()[1], context.thisWorld.getBackground()[2], context.thisWorld.getBackground()[3]);
				else GLES20.glClearColor(.5f,.3f,.4f,0);
				if(mEnableDepthBuffer) {
					clearMask |= GLES20.GL_DEPTH_BUFFER_BIT;
					GLES20.glEnable(GLES20.GL_DEPTH_TEST);
					GLES20.glDepthFunc(GLES20.GL_LESS);
					GLES20.glDepthMask(true);
					GLES20.glClearDepthf(1.0f);
				}

				GLES20.glClear(clearMask);


				if (context.thisWorld!=null){
					mVMatrix =  context.thisWorld.getCamera().getViewMatrix();
					for(Object3D o : context.thisWorld.getObjects()) 
						o.render(context.thisWorld.getCamera());
				}
			}
			if(context.capture){
				if(context.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
					capture(1280,752);
				else
					capture(800,616);
				//				800x616
				context.capture = false;
			}

		}
		public void capture(int mWidth , int mHeight){
			Log.e("Capture","again");
			IntBuffer ib = IntBuffer.allocate(mWidth*mHeight);
			IntBuffer ibt = IntBuffer.allocate(mWidth*mHeight);
			GLES20.glReadPixels(0, 0, mWidth, mHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, ib);

			// Convert upside down mirror-reversed image to right-side up normal image.
			for (int i = 0; i < mHeight; i++) {    
				for (int j = 0; j < mWidth; j++) {
					ibt.put((mHeight-i-1)*mWidth + j, ib.get(i*mWidth + j));
				}
			}                  

			mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
			mBitmap.copyPixelsFromBuffer(ibt);
			done = true;
		}
		public void onSurfaceChanged(GL10 gl, int width, int height) {
			if (!pause){
				mViewportWidth = width;
				mViewportHeight = height;
				if (context.thisWorld!=null)
					context.thisWorld.getCamera().setProjectionMatrix(new Point(width, height));
				GLES20.glViewport(0, 0, width, height);
			}
		}
	}

	public TabVizActivity getmContext() {
		return context;
	}

	public void setmContext(TabVizActivity context) {
		this.context = context;
	}




}
